﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using ISC.RIM.Silverlight;
using ISC.RIM.Silverlight.MapViewElement;

namespace OI.Controls
{
    public partial class DrillDownCallout : IMapViewChild, IMapViewElement
    {
        #region DATA
        public Point Anchor { get; private set; }
        public System.Action CloseAction { get; private set; }
        public Polygon Tail { get; private set; }
        #endregion

        public DrillDownCallout(Point anchor, double pixWidth, double pixHeight, object content, System.Action closeAction)
        {
            Anchor = anchor;
            Width = pixWidth;
            Height = pixHeight;
            CloseAction = closeAction;

            InitializeComponent();
            CalloutBody.Content = content;
        }


        // we always place the body center horizontal docked to top or bottom
        public void SetPosition(Point mapCenter)
        {
            if (!Utility.IsPointNull(Anchor) &&
                !Utility.IsPointNull(mapCenter))
            {
                double offsetX = -(Width * CalloutScaleTrans.ScaleX);
                double offsetY = -(Height * CalloutScaleTrans.ScaleY);

                // determine quadrant
                // Quadrant (LL)
                if (Anchor.X <= mapCenter.X && Anchor.Y <= mapCenter.Y)
                {
                    Canvas.SetLeft(this, 0);
                    Canvas.SetTop(this, offsetY);
                }
                // Quadrant (UL)
                else if (Anchor.X <= mapCenter.X && Anchor.Y > mapCenter.Y)
                {
                    Canvas.SetLeft(this, 0);
                    Canvas.SetTop(this, 0);
                }
                // Quadrant (LR)
                else if (Anchor.X > mapCenter.X && Anchor.Y <= mapCenter.Y)
                {
                    Canvas.SetLeft(this, offsetX);
                    Canvas.SetTop(this, offsetY);
                }
                // Quadrant (UR)
                else if (Anchor.X > mapCenter.X && Anchor.Y > mapCenter.Y)
                {
                    Canvas.SetLeft(this, offsetX);
                    Canvas.SetTop(this, 0);
                }

                UpdateTail(mapCenter);
            }
        }


        // draws the callout tail to the anchor
        public void UpdateTail(Point mapCenter)
        {
            if (Tail == null)
            {
                Tail = new Polygon
                {
                    Fill = new SolidColorBrush(Colors.White),
                    //Stroke = new SolidColorBrush(Color.FromArgb(190, 0, 0, 0)),
                    //StrokeThickness = 2,
                    //StrokeLineJoin = PenLineJoin.Round,
                    //StrokeEndLineCap = PenLineCap.Round,
                    //StrokeMiterLimit = 1
                };

                Canvas.SetZIndex(Tail, 200);

                var p = Parent as Canvas;
                if (p != null)
                {
                    p.Children.Add(Tail);
                }
            }

            Tail.Points.Clear();
            var aPt = new Point(Anchor.X - mapCenter.X, Anchor.Y - mapCenter.Y);
            Tail.Points.Add(aPt);

            // determine center and use polar transform
            var left = Canvas.GetLeft(this);
            var top = Canvas.GetTop(this);
            var width = Width * CalloutScaleTrans.ScaleX;
            var height = Height * CalloutScaleTrans.ScaleY;
            var centerOfBody = new Point((width / 2.0) + left, (height / 2.0) + top);
            var offset = Math.Min(Math.Abs(width), Math.Abs(height)) / 8;

            Tail.Points.Add(ComputeOffsetPoint(aPt, centerOfBody, offset, false));
            Tail.Points.Add(ComputeOffsetPoint(aPt, centerOfBody, offset, true));

            Tail.Points.Add(aPt);
            Tail.StrokeThickness = 2.0 * _LastxUPP;
        }


        // for tail attachment to callout
        // offset returned is against secord param
        private static Point ComputeOffsetPoint(Point p0a, Point p1a, double offset, bool direction)
        {
            var p0 = new ISC.MapDotNetServer.Common.Point(p0a.X, p0a.Y);
            var p1 = new ISC.MapDotNetServer.Common.Point(p1a.X, p1a.Y);

            double dx = p1.X - p0.X;
            double dy = p1.Y - p0.Y;

            // convert slope to angle and then rotate based on side of contour
            const double radConst = Math.PI / 2.0;
            double ang = Math.Atan2(dy, dx);
            ang += (direction ? radConst : -radConst);

            // convert radius and ang back to rectangular offsets
            double offX = offset * Math.Cos(ang);
            double offY = offset * Math.Sin(ang);

            // offset the point parameters
            p0.Offset(offX, offY);
            p1.Offset(offX, offY);

            return p1.ToSysWinPoint();
        }


        /// <summary>
        /// Removes this from its controller.
        /// </summary>
        public void Close()
        {
            // remove tail
            if (Tail != null)
            {
                var p = Parent as Canvas;
                if (p != null)
                {
                    p.Children.Remove(Tail);
                }
            }

            if (CloseAction != null)
            {
                CloseAction();
            }
        }


        // event handler
        private void HyperlinkButtonClick(object sender, RoutedEventArgs e)
        {
            Close();
        }


        #region IMapViewChild Memebers

        /// <summary>
        /// Update the position of the element based on the current map center.
        /// Note: the MapView transform group is set up so that center is 0,0 and scale is to map scale.
        /// </summary>
        /// <param name="mapCenter"></param>
        public void UpdatePositionBasedOnMapCenter(Point mapCenter)
        {
            _LastMapCenter = mapCenter;
            SetPosition(mapCenter);

        }
        private Point _LastMapCenter;


        /// <summary>
        /// Update size of element based on the current map zoom level.
        /// Note: the MapView transform group is set up so that center is 0,0 and scale is to map scale.
        /// </summary>
        /// <param name="zoomLevel">Zoom level as a double.</param>
        /// <param name="xUPP">X - map units per screen pixel</param>
        /// <param name="yUPP">Y - map units per screen pixel</param>
        public void UpdateSizeBasedOnMapZoomLevel(double zoomLevel, double xUPP, double yUPP)
        {
            // set actual size
            _LastxUPP = CalloutScaleTrans.ScaleX = xUPP;
            _LastyUPP = CalloutScaleTrans.ScaleY = -yUPP;
            
            // need to re-position after a size change
            SetPosition(_LastMapCenter);
        }
        private double _LastxUPP = 1.0;
        private double _LastyUPP = 1.0;

        #endregion
    }
}
